/**
 * @file ebex_hillclimbing.c
 *
 * @date Oct 19, 2011
 * @author seth
 *
 * @brief This file is part of EBEX State Estimator, created for the EBEX project
 *
 * This software is copyright (C) 2011 Columbia University
 *
 * EBEX State Estimator is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * EBEX State Estimator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EBEX State Estimator; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <ebex_genetic.h>

void ebex_set_hillclimbing_parameters(population_t *m_pop, ebex_mutate_allele_t m_mutate,
		genetic_iteration_hook_t m_hook, hill_climb_type_t m_type)
{

	if (!m_pop)
		die("Null pointer to population structure passed.");
	if (!m_mutate)
		die("Null pointer to GAmutate_allele callback passed.");

	if (m_pop->hc_params == NULL)
	{
		if (!(m_pop->hc_params = e_malloc(sizeof(hill_climb_t))))
			die("Unable to allocate memory");
	}

	m_pop->hc_params->mutate_allele = m_mutate;
	m_pop->hc_params->iteration_hook = m_hook;

	return;
}

int ebex_hillclimbing(population_t *m_pop, organism_t *m_best, const size_t m_max_iter)
{
	size_t iteration = 0; /* Current iteration number. */
	organism_t *putative; /* Current solution. */
	size_t chromo_id = 0; /* Chromosome number. */
	size_t allele_id = 0; /* Allele number. */

	/* Checks. */
	if (!m_pop)
		die("NULL pointer to population structure passed.");
	if (!m_pop->evaluate)
		die("Population's evaluation callback is undefined.");
	if (!m_pop->hc_params)
		die("Missing parameters.");
	if (!m_pop->hc_params->mutate_allele)
		die("Population's allele mutation callback is undefined.");

	/* Prepare working entity. */
	putative = genetic_get_new_organism(m_pop);

	/* Do we need to generate a random starting solution? */
	if (!m_best)
	{
		elog(log_level_verbose, "Will perform hill climbing with random starting solution.");

		m_best = genetic_get_new_organism(m_pop);
		genetic_organism_seed(m_pop, m_best);
	}
	else
	{
		elog(log_level_verbose, "Will perform hill climbing with specified starting solution.");
	}

	/*
	 * Ensure that initial solution is scored.
	 */
	if (m_best->fitness <= GENETIC_MIN_FITNESS) m_pop->evaluate(m_pop, m_best);

	elog(log_level_verbose, "Prior to the first iteration, the current solution has fitness score of %f", m_best->fitness);

	for (iteration = 0; iteration < m_max_iter; iteration++)
	{
		if(m_pop->hc_params->iteration_hook) m_pop->hc_params->iteration_hook(iteration, m_pop, m_best);
		iteration++;

		if (m_pop->hc_params->type == HILL_CLIMB_MODE_RANDOM)
		{
			chromo_id = ran_uint_uniform(m_pop->num_chromosomes);
			allele_id = ran_uint_uniform(m_best->chromosome[chromo_id].length);
		}
		else
		{
			allele_id++;
			if (allele_id >= m_best->chromosome[chromo_id].length)
			{
				allele_id = 0;
				chromo_id++;
				if (chromo_id >= m_pop->num_chromosomes)
					chromo_id = 0;
			}
		}


		/// Generate and score a new solution.
		m_pop->hc_params->mutate_allele(m_pop, m_best, putative, chromo_id, allele_id);
		m_pop->evaluate(m_pop, putative);


		if (putative->fitness > m_best->fitness)
		{
			EBEX_SWAP(m_best, putative);
		}

		elog(log_level_verbose, "After iteration %zu, the current solution has fitness score of %f", iteration, m_best->fitness);

	}

	genetic_free_organism(m_pop, putative);

	return iteration;
}
